#pragma once
#include <string>
#include <vector>
#include <iostream>
#include <unordered_map>
#include <fstream>
#include "util.hpp"
#include "log.hpp"
#include <mutex>
namespace Index{
    //正排的结构
    struct Forward_Mounted{
        uint64_t id;    //文档ID
        std::string title;//文档标题
        std::string content;//文档内容
        std::string url;//文档在Boost库中的链接
    };
    //倒排的结构
    struct Forback_Mounted{
       uint64_t id;
       std::string key_world;//关键字
       int weight;//搜索相关性
       Forback_Mounted():weight(0){}
    };
    using Forback=std::vector<Forback_Mounted>;
    // typedef std::vector<Forback_Mounted> Forback;
    //建立索引
    class index{
        private:
        std::vector<Forward_Mounted> Forward_List;   //建立正排索引结构
        std::unordered_map<std::string ,Forback> Forback_List;  //建立倒排索引结构
        public:
        //通过ID获得正排文档
        Forward_Mounted*GetForwardInfo(uint16_t id)
        {
            if(id>Forward_List.size())
            {
                std::cerr<<"Get ForwardInfo Failed !"<<std::endl;
                return nullptr;
            }
            return &(Forward_List[id]);
        }
        //通过key_world获取获取文档
        Forback*GetForbackInfo(std::string&key_world)
        {
            auto item=Forback_List.find(key_world);
            if(item==Forback_List.end())
            {
                std::cerr<<"Get ForbackInfo Failed !"<<std::endl;
                return nullptr;
            }
            return &(item->second);
        }

        //建立索引
        bool BuildIndex(const std::string&input)
        {
            std::ifstream in(input,std::ios::in|std::ios::binary);
            //打开我们之前存储好的html文件
            if(!in.is_open())
            {
                std::cerr<<"Open file failed !"<<std::endl;
                return false;
            }
            std::string line;
            //Debug
            int count=0;
            while(std::getline(in,line))
            {
                Forward_Mounted*Doc=BuildForwardIndex(line);    //先建立正排
                if(Doc==nullptr)
                {
                    std::cerr<<"BuiledForwardIndex Failed !"<<std::endl;
                    return false;
                }
                count++;
                //Debug
            if((count/20)==0) LOG(NORMAL,"正在建立索引");
            // std::cout<<"正在建立索引："<<count<<std::endl;
                //随后建立倒排
                BuildForbackIndex(*Doc);
            }
            LOG(NORMAL,"全部建立完毕！");
            return true;
        }

        //建立正排索引
        Forward_Mounted*BuildForwardIndex(std::string&line) //line: title \3 content \3 url \n
        {
            //存储划分好的文档结构title,content,url
            std::vector<std::string>Doc_List;
            //分隔符
            const std::string sep="\3";
            //引入jieba库进行字符串的分隔
            util::StringUtil::Splice(line,&Doc_List,sep);
            if(Doc_List.size()!=3)
                return nullptr;
            Forward_Mounted Doc;
            Doc.title=Doc_List[0];  //title
            Doc.content=Doc_List[1];    //content
            Doc.url=Doc_List[2];    //url
            Doc.id=Forward_List.size(); //ID
            Forward_List.push_back(std::move(Doc));
            return &Forward_List.back();   
        }
        //建立倒排
        void BuildForbackIndex(Forward_Mounted&Doc)
        {
            //统计词频--->为了计算相关性
            struct world_cnt{
                int title_cnt;
                int content_cnt;
                world_cnt():title_cnt(0),content_cnt(0){}
            };
            //统计字符对应的词频
            std::unordered_map<std::string,world_cnt>world_cnt_map;
            //title分割好后
            std::vector<std::string>title_cnt_v;
            //进行title词的分割
            util::JiebaUtil::CutString(Doc.title,&title_cnt_v);
            //统计title词频
            for(auto e1:title_cnt_v)
            {
                boost::to_lower(e1);        //无论大小写都要统计进去，所以为了统计都转化为小写
                world_cnt_map[e1].title_cnt++;
            }
            //content分割好后
            std::vector<std::string>content_cnt_v;
            //进行content词的分割
            util::JiebaUtil::CutString(Doc.content,&content_cnt_v);
            //统计content词频
            for(auto e2:content_cnt_v)
            {
                boost::to_lower(e2);
                world_cnt_map[e2].content_cnt++;
            }
#define X 10
#define Y 1            
            //进行倒排插入
            for(auto&e3:world_cnt_map)
            {
                Forback_Mounted FM_File;
                FM_File.id=Doc.id;
                FM_File.key_world=e3.first;
                FM_File.weight=X*e3.second.title_cnt+Y*e3.second.content_cnt;
                Forback&Tmp_Forback_List=Forback_List[e3.first];
                Tmp_Forback_List.push_back(std::move(FM_File));
            }
        }

        index(){};
        ~index(){};

        //获取单例==>创建index对象
         static index*Get_Singleton()
         {
            if(Index_Singleton==nullptr)
            {
                mtx.lock(); //加锁
                if(Index_Singleton==nullptr)
                    Index_Singleton=new index();
                mtx.unlock();//解锁
            }
            return Index_Singleton;
         }
        private:
        static index*Index_Singleton;   //静态变量，类里面声名，类外面定义
        static std::mutex mtx;  //设置锁
    };
    index*Index::Index_Singleton=nullptr;   
    std::mutex Index::mtx;  
} 
